#!/usr/bin/python3
# ******************************************************************************
# Copyright (c) Huawei Technologies Co., Ltd. 2021-2022. All rights reserved.
# licensed under the Mulan PSL v2.
# You can use this software according to the terms and conditions of the Mulan PSL v2.
# You may obtain a copy of Mulan PSL v2 at:
#     http://license.coscl.org.cn/MulanPSL2
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
# PURPOSE.
# See the Mulan PSL v2 for more details.
# ******************************************************************************/
import json
from http.client import RemoteDisconnected
from urllib.error import HTTPError, URLError
from asyncio import TimeoutError
import aiohttp
import wget
from fake_useragent import UserAgent
from retrying import retry
from constant import Constant
from exception import RequestError
from logger import logger

class Response:
    def __init__(self, error=None, response: aiohttp.ClientResponse = None, text=None) -> None:
        self.error = error
        self.response = response
        self.text = text

    @property
    def success(self):
        return self.response and (self.response.status == 200 or self.response.status == 201)

    @property
    def status_code(self):
        return self.response and self.response.status

    @property
    def json(self):
        if not self.response:
            return None
        try:
            return json.loads(self.text)
        except json.JSONDecodeError:
            return None


class Http:
    """
    async http request
    """
    user_agent = UserAgent()
    session = None

    def __init__(self, max_attempt_number=Constant.MAX_RETRY, stop_max_delay=Constant.MAX_DELAY) -> None:
        if self.session is None:
            self.session = aiohttp.ClientSession()
        self._max_attempt_number = max_attempt_number
        self._stop_max_delay = stop_max_delay
        self._response = Response()

    async def __aenter__(self):
        return self

    async def __aexit__(self, *args):
        await self.session.close()

    @property
    def headers(self):
        headers = {
            "User-Agent": self.user_agent.random
        }
        return headers

    def _set_headers(self, request_header):
        if "headers" not in request_header:
            request_header["headers"] = self.headers
        request_header["headers"].setdefault(
            "User-Agent", self.user_agent.random)
        request_header["headers"].setdefault(
            "Content-Type", "application/json")

    async def _get(self, url, params=None, **kwargs):
        self._set_headers(request_header=kwargs)

        @retry(stop_max_attempt_number=self._max_attempt_number, stop_max_delay=self._stop_max_delay)
        async def _http_request():
            return await self.session.get(url, params=params, timeout=Constant.CALL_MAX_DELAY, **kwargs)

        await self._request(_http_request)

    @classmethod
    async def get(cls, url, **kwargs) -> "Response":
        async with cls() as self:
            await self._get(url, **kwargs)
        return self._response

    async def _request(self, http_request):
        try:
            self._response.response = await http_request()
            self._response.text = await self._response.response.text()
        except aiohttp.ClientError as error:
            self._response.error = error
        except TimeoutError as error:
            raise RequestError(f'Call url timeout: {error}')

    async def _post(self, url, data=None, **kwargs):
        # self._set_headers(request_header=kwargs)
        @retry(stop_max_attempt_number=self._max_attempt_number, stop_max_delay=self._stop_max_delay)
        async def _http_request():
            return await self.session.post(url, data=data, timeout=Constant.CALL_MAX_DELAY, **kwargs)

        await self._request(_http_request)

    @classmethod
    async def post(cls, url, data=None, **kwargs) -> "Response":
        async with cls() as self:
            await self._post(url, data, **kwargs)
        return self._response

    def _dw(self, url, out_fname):
        @retry(stop_max_attempt_number=self._max_attempt_number, stop_max_delay=self._stop_max_delay)
        def getfile():
            file_name = wget.download(url, out_fname)
            if not file_name:
                raise
            return file_name

        return getfile()

    @staticmethod
    def download(url, out_fname, **kwargs):
        max_attempt_number = kwargs.get(
            "max_attempt_number", Constant.MAX_RETRY)
        stop_max_delay = kwargs.get(
            "stop_max_delay", Constant.MAX_DOWNLOAD_DELAY)

        @retry(stop_max_attempt_number=max_attempt_number, stop_max_delay=stop_max_delay)
        def _dw():
            try:
                file_name = wget.download(url, out_fname)
            except Exception as error:
                logger.error(
                f"download patch , message is {error}"
            )
                return None
            if not file_name:
                raise RequestError(f'Call url:"{url}" failed')
            return file_name

        try:
            return _dw()
        except (RequestError, RemoteDisconnected, HTTPError, URLError):
            return None


http = Http

__all__ = ("http",)
